import { FileTypeValidator, HttpException, HttpStatus, MaxFileSizeValidator, ParseFilePipe, ParseFilePipeBuilder, Post, Query, UploadedFile, UploadedFiles, UseInterceptors } from '@nestjs/common';
import { Controller, Get } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { AppService } from './app.service';
import { PublicRoute } from './decorations/PublicRoute';
import { Express } from 'express'
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { FileSizeValidationPipe } from './decorations/ImageValid';
import { HttpService } from '@nestjs/axios';
import { map } from 'rxjs/operators';
import { RequireRole, Role } from './decorations/Role';
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from "crypto";
import { promisify } from "util";
import * as bcrypt from 'bcrypt';
import * as dayjs from 'dayjs';
import { Throttle } from '@nestjs/throttler';
import Utils from './utils/Utils';

@PublicRoute()
@ApiTags('nestjs 学习')
@Controller('test')
export class AppController {
  constructor(
    private readonly appService: AppService,
    private readonly httpService: HttpService,
  ) { }

  @ApiOperation({ summary: '路径匹配' })
  @Get('/he*lo')
  getHello(): string {
    return this.appService.getHello();
  }

  @ApiOperation({ summary: '错误返回' })
  @Get('throw/HttpException')
  testDemo(): string {
    throw new HttpException('手动抛出错误', HttpStatus.CONFLICT)
  }

  @ApiOperation({ summary: 'Post请求' })
  @Post('post')
  testPost(): string {
    return 'This is a POST request!'
  }

  @ApiOperation({ summary: '队列，添加队列' })
  @Get('add/bullmq')
  addValToBullMq(@Query('name') name: string) {
    this.appService.addValToBullMq(name)
  }

  @ApiOperation({ summary: '事件触发' })
  @Get('trigger/emitter')
  triggerEmitter(@Query('orderId') orderId: number) {
    this.appService.triggerEmitter(orderId)
  }

  @ApiOperation({ summary: '普通上传' })
  @Post('upload/file')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    return 'data:image/png;base64,' + file.buffer.toString('base64')
  }

  @ApiOperation({ summary: '文件验证，自定义方式，上传大小限制' })
  @Post('upload/file/valid/size')
  @UseInterceptors(FileInterceptor('file'))
  fileSizeValidationPipe(@UploadedFile(new FileSizeValidationPipe()) file: Express.Multer.File) {
    return 'data:image/png;base64,' + file.buffer.toString('base64')
  }

  @ApiOperation({ summary: '文件验证，内置管道方式，上传类型限制' })
  @Post('upload/file/valid/type')
  @UseInterceptors(FileInterceptor('file'))
  fileTypeValidationPipe(
    @UploadedFile(new ParseFilePipe({
      validators: [
        // new MaxFileSizeValidator({ maxSize: 1000 }), // B
        new FileTypeValidator({ fileType: new RegExp(/image\/(jpeg|jpg|png)/) }),
      ],
    }),
    )
    file: Express.Multer.File) {
    return 'data:image/png;base64,' + file.buffer.toString('base64')
  }

  @ApiOperation({ summary: '文件验证，组合验证器，上传类型类型、大小限制' })
  @Post('upload/file/valid/typeAndSize')
  @UseInterceptors(FileInterceptor('file'))
  fileSizeTypeValidationPipe(
    @UploadedFile(
      new ParseFilePipeBuilder()
        .addFileTypeValidator({
          fileType: 'png',
        })
        .addMaxSizeValidator({
          maxSize: 1000 // B
        })
        .build({
          errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY
        }),
    )
    file: Express.Multer.File) {
    return 'data:image/png;base64,' + file.buffer.toString('base64')
  }

  @ApiOperation({ summary: '文件上传，多文件' })
  @Post('upload/files')
  @UseInterceptors(FilesInterceptor('files'))
  uploadFiles(@UploadedFiles() files: Array<Express.Multer.File>) {
    const result = files.reduce((arr, file) =>
      arr.push('data:image/png;base64,' + file.buffer.toString('base64')) && arr
      , [])
    return result
  }

  @ApiOperation({ summary: 'HttpModule 使用' })
  @Get('http/baidu')
  httpModuleToBaiDu() {
    return this.httpService.get('http://www.baidu.com').pipe(map(res => res.data))
  }


  @ApiOperation({ summary: 'HttpModule 使用原生 axios' })
  @Get('axios/baidu')
  async axiosToBaiDu() {
    const result = await this.httpService.axiosRef.get('http://www.baidu.com')
    return result.data
  }

  @ApiOperation({ summary: '鉴权，管理员可调用' })
  @RequireRole(Role.Admin)
  @Get('role/admin')
  roleByAdminCall() {
    return '您是管理员，可以调用~'
  }

  @ApiOperation({ summary: '加密、解密' })
  @Get('crypto/text')
  async cryptoText(@Query('text') text: string) {
    const textToEncrypt = text; // 明文
    const iv = randomBytes(16); // 生成指定长度的随机字节
    const password = "Password used to generate key"; // 密钥
    const salt = "salt"; // 随机盐

    const key = (await promisify(scrypt)(password, salt, 32)) as Buffer; // 密匙
    const cipher = createCipheriv("aes-256-ctr", key, iv); // 加密对象

    const encryptedText = Buffer.concat([
      cipher.update(textToEncrypt),
      cipher.final(),
    ]); // 密文

    // 解密
    const decipher = createDecipheriv("aes-256-ctr", key, iv); // 解密对象
    const decryptedText = Buffer.concat([
      decipher.update(encryptedText),
      decipher.final(),
    ]);

    return {
      text: textToEncrypt,
      encryptedText: encryptedText.toString('base64'),
      decryptedText: decryptedText.toString(),
    }
  }

  @ApiOperation({ summary: '哈希' })
  @Get('bcrypt/text')
  async bcryptText(@Query('text') text: string) {
    const saltOrRounds = 10;
    const password = text;
    const hash = await bcrypt.hash(password, saltOrRounds); // 哈希值
    const salt = await bcrypt.genSalt(); // 盐
    const isMatch = await bcrypt.compare(password, hash); // 值匹配

    return {
      text,
      hash,
      hashLen: hash.length,
      salt,
      isMatch
    }
  }

  async throttlerHttp(count: number, url: string) {
    const _count = count || 10
    const starTime = dayjs().format('YYYY-MM-DD HH:mm:ss')

    const result = []
    for (let index = 0; index < _count; index++) {
      const _url = `${url}?text=${index}`
      const temp = await this.httpService.axiosRef.get(_url)
      const time = dayjs().format('YYYY-MM-DD HH:mm:ss')
      result.push({ key: time, url, value: temp.data.data })
    }
    const endTime = dayjs().format('YYYY-MM-DD HH:mm:ss')

    return { starTime, result, endTime }
  }

  @ApiOperation({ summary: '模拟批量请求' })
  @Get('throttler/global/limit')
  async launchGlobalTest(@Query('count') count: number) {
    const url = `http://${Utils.ipv4()}:8000/test/throttler/global`
    return await this.throttlerHttp(count, url)
  }

  @ApiOperation({ summary: '限流，全局配置每秒最多10个请求，超出报错429' })
  @Get('throttler/global')
  async throttlerText(@Query('text') text: string) {
    return text
  }

  @ApiOperation({ summary: '模拟批量请求' })
  @Get('throttler/customization/limit')
  async launchCustomizationTest(@Query('count') count: number) {
    const url = `http://${Utils.ipv4()}:8000/test/throttle/customization`
    return await this.throttlerHttp(count, url)
  }

  @ApiOperation({ summary: '@Throttle()定制限流，每分钟10个，超出报错429' })
  @Throttle({ default: { limit: 10, ttl: 60 * 1000 } })
  @Get('throttle/customization')
  throttleCustomization(@Query('text') text: string) {
    return text
  }
}
